﻿using ApproveFlowEngine.EntityFrameworkCore;
using ApproveFlowEngine.EntityFrameworkCoreSplitTable.EFCore.Core;

using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
using Volo.Abp.Uow.EntityFrameworkCore;

namespace ApproveFlowEngine.EntityFrameworkCoreSplitTable.AbpCore
{
    public class SplitRepositoryProvider<TDbContext> : UnitOfWorkDbContextProvider<TDbContext> where TDbContext : IEfCoreDbContext
    {
        #region Public Constructors

        public SplitRepositoryProvider(IUnitOfWorkManager unitOfWorkManager, IConnectionStringResolver connectionStringResolver, ICancellationTokenProvider cancellationTokenProvider, ICurrentTenant currentTenant, IEfCoreDbContextTypeProvider efCoreDbContextTypeProvider) : base(unitOfWorkManager, connectionStringResolver, cancellationTokenProvider, currentTenant, efCoreDbContextTypeProvider)
        {
        }

        #endregion Public Constructors



        #region Public Methods

        public override async Task<TDbContext> GetDbContextAsync()
        {
            var dbContextType = typeof(TDbContext);

            var unitOfWork = UnitOfWorkManager.Current ?? throw new AbpException("A DbContext can only be created inside a unit of work!");
            var targetDbContextType = EfCoreDbContextTypeProvider.GetDbContextType(typeof(TDbContext));
            var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(targetDbContextType);
            var text = await ResolveConnectionStringAsync(connectionStringName).ConfigureAwait(continueOnCapturedContext: false);
            var dbContextKey = targetDbContextType.FullName + "_" + text;
            if (dbContextType.IsAssignableTo<SplitDbContext>())
            {
                dbContextKey = dbContextKey + "_" + TableTailHelper.GetKey();
            }
            var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
            if (databaseApi == null)
            {
                TDbContext dbContext;
                var transactionApiKey = "EntityFrameworkCore_" + DbContextCreationContext.Current?.ConnectionString;
                var transactionApi = unitOfWork.FindTransactionApi(transactionApiKey);
                if (dbContextType.IsAssignableTo<SplitDbContext>() && unitOfWork.Options.IsTransactional && transactionApi is null)
                {
                    // 解决同一个工作单元中并发创建事务性多个相同连接字符串的db上下文时的并发错误
                    lock (unitOfWork)
                    {
                        dbContext = CreateDbContextAsync(unitOfWork, connectionStringName, text).Result;
                    }
                }
                else
                {
                    dbContext = await CreateDbContextAsync(unitOfWork, connectionStringName, text).ConfigureAwait(continueOnCapturedContext: false);
                }
                databaseApi = new EfCoreDatabaseApi(dbContext);
                unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
            }

            return (TDbContext)((EfCoreDatabaseApi)databaseApi).DbContext;
        }

        #endregion Public Methods
    }
}